/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 *
 * This software is published under the terms of Open Source
 * License version 1.1, a copy of which has been included with 
 * this distribution in the License.txt file.
 */

package jdbcmanager.table;

import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;

import jdbcmanager.app.App;
import jdbcmanager.app.StrWise;

import com.sun.rowset.JdbcRowSetImpl;

/**
 * 
 * @author Francisco Morero
 */
final class GridTableModel extends AbstractTableModel
{
    private static final String sToolTipTemplate;
    
	private String[]          asColName     = null;
	private boolean[]         abColEditable = null; // Is a column editable?
	private JdbcRowSetImpl    rowset        = null;
	private ResultSetMetaData rsmd          = null;
	private boolean           bEditable     = false;
	private int               nRecCount     = 0;

    static
    {
        sToolTipTemplate = new StringBuffer( 512 )
            .append( "<html><body>" )
            .append( "&#32;<u><b><font color=\"#000080\">{Field}</font></b></u><br>" )
            .append( "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">" )
            .append( "  <tr>" )
            .append( "    <td>&#32;Type</td>" )
            .append( "    <td>&#32;<b>{Type}</b>&#32;</td>" )
            .append( "  </tr>" )
            .append( "  <tr>" )
            .append( "    <td>&#32;Nullable</td>" )
            .append( "    <td>&#32;<b>{Nullable}</b>&#32;</td>" )
            .append( "  </tr>" )
            .append( "  <tr>" )
            .append( "    <td>&#32;Searchable</td>" )
            .append( "    <td>&#32;<b>{Searchable}</b>&#32;</td>" )
            .append( "  </tr>" )
            .append( "  <tr>" )
            .append( "    <td>&#32;Display size</td>" )
            .append( "    <td>&#32;<b>{DispSize}</b>&#32;</td>" )
            .append( "  </tr>" )
            .append( "</table>" )
            .append( "</body></html>" ).toString();
    }
    
    //------------------------------------------------------------------------//

	GridTableModel( JdbcRowSetImpl rowset, ResultSetMetaData rsmd )
	{		
		this.rowset = rowset;
        this.rsmd   = rsmd;
        
        try
		{
			// Column names
			this.asColName = new String[ this.rsmd.getColumnCount() ];

			for( int n = 0; n < this.asColName.length; n++ )
				this.asColName[n] = this.rsmd.getColumnLabel( n + 1 );

			// Editable columns
			this.abColEditable = new boolean[ this.rsmd.getColumnCount() ];

			for( int n = 0; n < this.asColName.length; n++ )
				this.abColEditable[n] = ! this.rsmd.isReadOnly( n + 1 );

			// Find record count
			this.rowset.last();
			this.nRecCount = this.rowset.getRow();
			this.rowset.first();

			// Needed to be done in this way
			if( this.rowset.getMaxRows() != 0 )
				this.nRecCount = Math.min( this.nRecCount, this.rowset.getMaxRows() );
		}
		catch( SQLException exc )
		{
			App.showException( exc, "Error accessing 'ResultSet MetaData'" );
		}
	}

	public int getColumnCount()
	{
		return this.asColName.length;
	}

	public String getColumnName( int nCol )
	{
		return this.asColName[nCol];
	}

	public int getRowCount()
	{
		return this.nRecCount;
	}

	public Object getValueAt( int nRow, int nCol )
	{
		Object oValue = null;

		try
		{
			this.rowset.absolute( nRow + 1 );
			
            /* TODO: hay q hacer algo con los BLOB, CLOB y LONGVARBINARY
            int nColType = this.rsmd.getColumnType( nCol + 1 );
            
			if( nColType == Types.BLOB || nColType == Types.LONGVARBINARY ) 
				System.out.println(asColName[nCol]+" == "+ nColType +"("+this.rsmd.getColumnTypeName( nCol + 1 )+")" );
            */
				
			oValue = this.rowset.getObject( nCol + 1 );
		}
		catch( SQLException exc )
		{
			oValue = "Error";
			App.showDebugException( exc );
		}

		return oValue;
	}

	public boolean isCellEditable( int nRow, int nCol )
	{
		return this.bEditable && this.abColEditable[nCol];
	}

	public void setValueAt( Object oValue, int nRow, int nCol )
	{
		try
		{
			this.rowset.absolute( nRow + 1 ); // Need to do it because cursor can be anywhere
			this.rowset.updateObject( nCol + 1, oValue );
			this.rowset.updateRow();
			this.rowset.refreshRow();
			fireTableChanged( new TableModelEvent( this, nRow ) );
		}
		catch( Exception exc )
		{
			App.showException( exc );
		}
	}

	// JTable uses this method to determine the default renderer/editor for each cell
	public Class getColumnClass( int nCol )
	{
		Object value = getValueAt(0, nCol);
		return (value == null) ? String.class : value.getClass();
	}

	//----------------------------------------------------------------------//

	void rowAdded( int nRow )
	{
		fireTableRowsInserted( nRow, nRow );
		this.nRecCount++;
	}

	void rowDeleted( int nRow )
	{
		fireTableRowsDeleted( nRow, nRow );
		this.nRecCount--;
	}
	
	void setEditable( boolean bEditable )
	{
		this.bEditable = bEditable;
	}
	
	String createTooltip( int nCol )
	{
		String  sType        = "<unknown>";
		String  sNullable    = "<unknown>";
		boolean bSearchable  = false;
		String  sDisplaySize = "<unknown>";
        String  sRet         = sToolTipTemplate;   // Do not need to make  sToolTipTemplate.clone()
        
        int nNullable    = -1;
        int nDisplaySize = -1;
		
		try{ sType        = this.rsmd.getColumnTypeName(    nCol+1 ); } catch( SQLException exc ) { /* Nothing to do */ }
		try{ nNullable    = this.rsmd.isNullable(           nCol+1 ); } catch( SQLException exc ) { /* Nothing to do */ }
		try{ bSearchable  = this.rsmd.isSearchable(         nCol+1 ); } catch( SQLException exc ) { /* Nothing to do */ }
		try{ nDisplaySize = this.rsmd.getColumnDisplaySize( nCol+1 ); } catch( SQLException exc ) { /* Nothing to do */ }
		
		if( nNullable != -1 )
			sNullable = (nNullable == ResultSetMetaData.columnNullable) ? "true" : "false";
		
		if( nDisplaySize != -1 )
			sDisplaySize = String.valueOf( nDisplaySize );
        
        sRet = StrWise.replaceAll( sRet, "{Field}"     , getColumnName( nCol ) );
        sRet = StrWise.replaceAll( sRet, "{Type}"      , sType );
        sRet = StrWise.replaceAll( sRet, "{Nullable}"  , sNullable );
        sRet = StrWise.replaceAll( sRet, "{Searchable}", bSearchable ? "true" : "false" );
        sRet = StrWise.replaceAll( sRet, "{DispSize}"  , sDisplaySize );

        return sRet;
	}
}